Skip to content

feat(dashboard): Allow creating customer and address inline on draft …#4952

Open
Ryrahul wants to merge 2 commits into
vendurehq:masterfrom
Ryrahul:feat/dashboard-draft-order-inline-customer-address
Open

feat(dashboard): Allow creating customer and address inline on draft …#4952
Ryrahul wants to merge 2 commits into
vendurehq:masterfrom
Ryrahul:feat/dashboard-draft-order-inline-customer-address

Conversation

@Ryrahul

@Ryrahul Ryrahul commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Brings the React dashboard draft order flow to parity with the Angular admin-ui by allowing an admin to create a new customer inline and enter a new/ad-hoc shipping or billing address inline, rather than only selecting existing records.

  • CustomerSelector gains an opt-in allowCreateNew mode rendering a tabbed popover (select existing / create new) that emits a CreateCustomerInput via onCreateNew.
  • CustomerAddressSelector gains a "New address" tab embedding the shared CustomerAddressForm, emitting a CreateAddressInput via onSubmitNew.
  • CustomerAddressForm gains a hideDefaultAddressFlags prop (the default shipping/billing flags are not applicable in the draft order context).
  • Wired into both the draft order page and the order modification page.

No backend changes required: setCustomerForDraftOrder already accepts a CreateCustomerInput, and the draft order address mutations accept a CreateAddressInput.

Fixes #4951

Description

The React dashboard draft order flow previously only allowed selecting an existing customer and selecting one of the customer's saved addresses. The old Angular admin-ui also supported creating a new customer inline and entering a new/ad-hoc address inline. This PR restores that parity.

The backend already fully supports this — setCustomerForDraftOrder accepts an optional CreateCustomerInput, and setDraftOrderShippingAddress / setDraftOrderBillingAddress accept a CreateAddressInput. The React UI simply never used these paths. This PR wires them up:

  • CustomerCustomerSelector gets an opt-in allowCreateNew tabbed popover ("Existing customer" / "Create new customer"). The create form has the same fields as Angular (title, first name, last name, email, phone). The existing select-only behaviour is preserved when allowCreateNew is not set, so the customer-groups call site is untouched.
  • AddressCustomerAddressSelector gets an "Existing address" / "New address" tabbed popover. The new-address tab reuses the shared CustomerAddressForm (default-shipping/billing checkboxes hidden via a new hideDefaultAddressFlags prop, matching Angular's draft flow). Works with or without a customer set.
  • Both the draft order page and the order modification page are wired to the inline-create paths.

Added e2e coverage in sales/orders.spec.ts for creating a customer inline and entering a new shipping address inline. All 20 order e2e tests pass.

Breaking changes

None. The change is additive and backward-compatible — CustomerSelector keeps its original select-only behaviour by default (inline creation is opt-in via allowCreateNew), and no backend/GraphQL changes are required.

Screenshots

image image

Checklist

📌 Always:

  • I have set a clear title
  • My PR is small and contains a single feature
  • I have checked my own PR

👍 Most of the time:

  • I have added or updated test cases
  • I have updated the README if needed

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vendure-storybook Ready Ready Preview, Comment Jul 10, 2026 9:40pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 38356eab-3777-4040-bccb-f8db2a290b3c

📥 Commits

Reviewing files that changed from the base of the PR and between cfa605f and 18b5714.

📒 Files selected for processing (2)
  • packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx
  • packages/dashboard/src/lib/components/shared/customer-selector.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx
  • packages/dashboard/src/lib/components/shared/customer-selector.tsx

📝 Walkthrough

Walkthrough

The React dashboard adds inline customer creation to draft orders and inline ad-hoc address entry to draft-order and order-modification flows. Customer and address selectors use tabbed popovers with typed submission callbacks and address-form mapping. Draft-order mutations receive new customer and address inputs, while order modification exposes raw address update handlers. End-to-end tests cover both flows, and localization catalogs are regenerated with updated references and UI strings.

Possibly related PRs

Suggested reviewers: michaelbromley, BibiSebi, dlhck

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main dashboard feature: inline customer and address creation on draft orders.
Description check ✅ Passed The description includes the summary, linked issue, breaking changes, screenshots, and checklist required by the template.
Linked Issues check ✅ Passed The changes implement the inline customer and address flows and wire them into draft order and order modification paths as requested in #4951.
Out of Scope Changes check ✅ Passed The locale catalog updates and selector/form changes align with the new inline customer and address UI and do not appear unrelated.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…orders

Brings the React dashboard draft order flow to parity with the Angular
admin-ui by allowing an admin to create a new customer inline and enter a
new/ad-hoc shipping or billing address inline, rather than only selecting
existing records.

- CustomerSelector gains an opt-in `allowCreateNew` mode rendering a tabbed
  popover (select existing / create new) that emits a CreateCustomerInput via
  `onCreateNew`.
- CustomerAddressSelector gains a "New address" tab embedding the shared
  CustomerAddressForm, emitting a CreateAddressInput via `onSubmitNew`.
- CustomerAddressForm gains a `hideDefaultAddressFlags` prop (the default
  shipping/billing flags are not applicable in the draft order context).
- Wired into both the draft order page and the order modification page.

No backend changes required: setCustomerForDraftOrder already accepts a
CreateCustomerInput, and the draft order address mutations accept a
CreateAddressInput.

Relates to vendurehq#4838
@Ryrahul Ryrahul force-pushed the feat/dashboard-draft-order-inline-customer-address branch from b8092ae to cfa605f Compare July 10, 2026 21:21

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx (2)

44-48: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Opt out of placeholderData: keepPreviousData for this identity-keyed query.

queryKey: ['customerAddresses', customerId] carries identity, matching the guideline's own ['customerGroup', id] example. Without opting out, switching to a different/newly-created customer can momentarily render the previous customer's address list before the new query resolves.

🛡️ Proposed fix
     const { data, isLoading } = useQuery<CustomerAddressesQuery>({
         queryKey: ['customerAddresses', customerId],
         queryFn: () => api.query(getCustomerAddressesDocument, { customerId: customerId ?? '' }),
         enabled: !!customerId,
+        placeholderData: undefined,
     });

Based on learnings, "React Query: placeholderData: keepPreviousData is configured globally. Opt out per-query (placeholderData: undefined) when the queryKey carries identity (e.g. ['customerGroup', id]) and showing previous identity's data would be misleading."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx`
around lines 44 - 48, Disable the globally configured keep-previous-data
behavior for the identity-keyed query in the customer address selector by
setting placeholderData to undefined in the useQuery options for
customerAddresses. Keep the customerId-based queryKey and existing loading
behavior unchanged.

Source: Coding guidelines


44-54: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Tab auto-switches away from "New address" once the addresses query resolves.

canSelectExisting is computed from addresses.length > 0, which is always 0 while isLoading is true — so during loading the tab is forced to 'new', then flips back to the stored activeTab ('existing' by default) as soon as the query resolves and the customer turns out to have saved addresses. This can happen right after selecting/creating a customer (this PR's own new flow) and yanks the user out of the "New address" tab mid-entry.

🐛 Proposed fix
-    const canSelectExisting = !!customerId && addresses.length > 0;
+    const canSelectExisting = !!customerId && (isLoading || addresses.length > 0);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx`
around lines 44 - 54, Prevent the selected tab from changing when the
customer-address query transitions from loading to loaded. Update the
tab-selection logic around canSelectExisting and effectiveTab in the customer
address selector to preserve an explicitly chosen “new” tab during loading and
after addresses are fetched, only defaulting to “existing” when appropriate for
a newly initialized customer.
🧹 Nitpick comments (2)
packages/dashboard/src/lib/components/shared/customer-selector.tsx (2)

70-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Validation error messages bypass the i18n system.

createCustomerFormSchema hardcodes English error strings ('First name is required', 'Last name is required', 'Email address is required') instead of using Trans/useLingui's t like the rest of this component (e.g. placeholders, labels). Given the huge number of locale catalogs in this repo, these validation messages will always render in English regardless of the admin's selected language.

♻️ Suggested fix using useLingui's `t`
-const createCustomerFormSchema = z.object({
-    title: z.string().optional(),
-    firstName: z.string().min(1, { message: 'First name is required' }),
-    lastName: z.string().min(1, { message: 'Last name is required' }),
-    emailAddress: z.string().min(1, { message: 'Email address is required' }).email(),
-    phoneNumber: z.string().optional(),
-});
+function useCreateCustomerFormSchema() {
+    const { t } = useLingui();
+    return z.object({
+        title: z.string().optional(),
+        firstName: z.string().min(1, { message: t`First name is required` }),
+        lastName: z.string().min(1, { message: t`Last name is required` }),
+        emailAddress: z.string().min(1, { message: t`Email address is required` }).email(),
+        phoneNumber: z.string().optional(),
+    });
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/dashboard/src/lib/components/shared/customer-selector.tsx` around
lines 70 - 78, Replace the hardcoded English validation messages in
createCustomerFormSchema with localized messages using the component’s
useLingui/t pattern. Ensure the schema is created where the translation function
is available, and apply localized text to the firstName, lastName, and
emailAddress required validations.

70-76: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Zod .min() message option is deprecated in v4 in favor of error.

Confirmed via Zod v4 migration docs: the message param is "still supported but deprecated," replaced by the unified error parameter. Functionally fine today, but worth migrating for forward compatibility.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/dashboard/src/lib/components/shared/customer-selector.tsx` around
lines 70 - 76, Update the createCustomerFormSchema validation rules to use Zod
v4’s error option instead of the deprecated message option for the firstName,
lastName, and emailAddress min-length checks, preserving the existing validation
text and behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/dashboard/src/lib/components/shared/customer-selector.tsx`:
- Around line 196-283: Reset activeTab to 'existing' whenever the popover is
closed programmatically: update the onSelect callback in CustomerSearch and the
onSubmit callback in CreateCustomerForm to call setActiveTab('existing')
alongside setOpen(false). Keep the existing onOpenChange reset for user-driven
closures.

---

Outside diff comments:
In
`@packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx`:
- Around line 44-48: Disable the globally configured keep-previous-data behavior
for the identity-keyed query in the customer address selector by setting
placeholderData to undefined in the useQuery options for customerAddresses. Keep
the customerId-based queryKey and existing loading behavior unchanged.
- Around line 44-54: Prevent the selected tab from changing when the
customer-address query transitions from loading to loaded. Update the
tab-selection logic around canSelectExisting and effectiveTab in the customer
address selector to preserve an explicitly chosen “new” tab during loading and
after addresses are fetched, only defaulting to “existing” when appropriate for
a newly initialized customer.

---

Nitpick comments:
In `@packages/dashboard/src/lib/components/shared/customer-selector.tsx`:
- Around line 70-78: Replace the hardcoded English validation messages in
createCustomerFormSchema with localized messages using the component’s
useLingui/t pattern. Ensure the schema is created where the translation function
is available, and apply localized text to the firstName, lastName, and
emailAddress required validations.
- Around line 70-76: Update the createCustomerFormSchema validation rules to use
Zod v4’s error option instead of the deprecated message option for the
firstName, lastName, and emailAddress min-length checks, preserving the existing
validation text and behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 894f94a2-ab65-41a5-9621-be977d4f371e

📥 Commits

Reviewing files that changed from the base of the PR and between 565d8a8 and b8092ae.

📒 Files selected for processing (35)
  • packages/dashboard/e2e/tests/sales/orders.spec.ts
  • packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx
  • packages/dashboard/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx
  • packages/dashboard/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx
  • packages/dashboard/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts
  • packages/dashboard/src/i18n/locales/ar.po
  • packages/dashboard/src/i18n/locales/bg.po
  • packages/dashboard/src/i18n/locales/cs.po
  • packages/dashboard/src/i18n/locales/de.po
  • packages/dashboard/src/i18n/locales/en.po
  • packages/dashboard/src/i18n/locales/es.po
  • packages/dashboard/src/i18n/locales/fa.po
  • packages/dashboard/src/i18n/locales/fr.po
  • packages/dashboard/src/i18n/locales/he.po
  • packages/dashboard/src/i18n/locales/hr.po
  • packages/dashboard/src/i18n/locales/hu.po
  • packages/dashboard/src/i18n/locales/it.po
  • packages/dashboard/src/i18n/locales/ja.po
  • packages/dashboard/src/i18n/locales/nb.po
  • packages/dashboard/src/i18n/locales/ne.po
  • packages/dashboard/src/i18n/locales/nl.po
  • packages/dashboard/src/i18n/locales/pl.po
  • packages/dashboard/src/i18n/locales/pt_BR.po
  • packages/dashboard/src/i18n/locales/pt_PT.po
  • packages/dashboard/src/i18n/locales/ro.po
  • packages/dashboard/src/i18n/locales/ru.po
  • packages/dashboard/src/i18n/locales/sv.po
  • packages/dashboard/src/i18n/locales/tr.po
  • packages/dashboard/src/i18n/locales/uk.po
  • packages/dashboard/src/i18n/locales/uz.po
  • packages/dashboard/src/i18n/locales/zh_Hans.po
  • packages/dashboard/src/i18n/locales/zh_Hant.po
  • packages/dashboard/src/lib/components/shared/customer-address-form.tsx
  • packages/dashboard/src/lib/components/shared/customer-selector.tsx
  • packages/dev-server/graphql/graphql-env.d.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx (2)

44-48: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Opt out of placeholderData: keepPreviousData for this identity-keyed query.

queryKey: ['customerAddresses', customerId] carries identity, matching the guideline's own ['customerGroup', id] example. Without opting out, switching to a different/newly-created customer can momentarily render the previous customer's address list before the new query resolves.

🛡️ Proposed fix
     const { data, isLoading } = useQuery<CustomerAddressesQuery>({
         queryKey: ['customerAddresses', customerId],
         queryFn: () => api.query(getCustomerAddressesDocument, { customerId: customerId ?? '' }),
         enabled: !!customerId,
+        placeholderData: undefined,
     });

Based on learnings, "React Query: placeholderData: keepPreviousData is configured globally. Opt out per-query (placeholderData: undefined) when the queryKey carries identity (e.g. ['customerGroup', id]) and showing previous identity's data would be misleading."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx`
around lines 44 - 48, Disable the globally configured keep-previous-data
behavior for the identity-keyed query in the customer address selector by
setting placeholderData to undefined in the useQuery options for
customerAddresses. Keep the customerId-based queryKey and existing loading
behavior unchanged.

Source: Coding guidelines


44-54: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Tab auto-switches away from "New address" once the addresses query resolves.

canSelectExisting is computed from addresses.length > 0, which is always 0 while isLoading is true — so during loading the tab is forced to 'new', then flips back to the stored activeTab ('existing' by default) as soon as the query resolves and the customer turns out to have saved addresses. This can happen right after selecting/creating a customer (this PR's own new flow) and yanks the user out of the "New address" tab mid-entry.

🐛 Proposed fix
-    const canSelectExisting = !!customerId && addresses.length > 0;
+    const canSelectExisting = !!customerId && (isLoading || addresses.length > 0);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx`
around lines 44 - 54, Prevent the selected tab from changing when the
customer-address query transitions from loading to loaded. Update the
tab-selection logic around canSelectExisting and effectiveTab in the customer
address selector to preserve an explicitly chosen “new” tab during loading and
after addresses are fetched, only defaulting to “existing” when appropriate for
a newly initialized customer.
🧹 Nitpick comments (2)
packages/dashboard/src/lib/components/shared/customer-selector.tsx (2)

70-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Validation error messages bypass the i18n system.

createCustomerFormSchema hardcodes English error strings ('First name is required', 'Last name is required', 'Email address is required') instead of using Trans/useLingui's t like the rest of this component (e.g. placeholders, labels). Given the huge number of locale catalogs in this repo, these validation messages will always render in English regardless of the admin's selected language.

♻️ Suggested fix using useLingui's `t`
-const createCustomerFormSchema = z.object({
-    title: z.string().optional(),
-    firstName: z.string().min(1, { message: 'First name is required' }),
-    lastName: z.string().min(1, { message: 'Last name is required' }),
-    emailAddress: z.string().min(1, { message: 'Email address is required' }).email(),
-    phoneNumber: z.string().optional(),
-});
+function useCreateCustomerFormSchema() {
+    const { t } = useLingui();
+    return z.object({
+        title: z.string().optional(),
+        firstName: z.string().min(1, { message: t`First name is required` }),
+        lastName: z.string().min(1, { message: t`Last name is required` }),
+        emailAddress: z.string().min(1, { message: t`Email address is required` }).email(),
+        phoneNumber: z.string().optional(),
+    });
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/dashboard/src/lib/components/shared/customer-selector.tsx` around
lines 70 - 78, Replace the hardcoded English validation messages in
createCustomerFormSchema with localized messages using the component’s
useLingui/t pattern. Ensure the schema is created where the translation function
is available, and apply localized text to the firstName, lastName, and
emailAddress required validations.

70-76: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Zod .min() message option is deprecated in v4 in favor of error.

Confirmed via Zod v4 migration docs: the message param is "still supported but deprecated," replaced by the unified error parameter. Functionally fine today, but worth migrating for forward compatibility.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/dashboard/src/lib/components/shared/customer-selector.tsx` around
lines 70 - 76, Update the createCustomerFormSchema validation rules to use Zod
v4’s error option instead of the deprecated message option for the firstName,
lastName, and emailAddress min-length checks, preserving the existing validation
text and behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/dashboard/src/lib/components/shared/customer-selector.tsx`:
- Around line 196-283: Reset activeTab to 'existing' whenever the popover is
closed programmatically: update the onSelect callback in CustomerSearch and the
onSubmit callback in CreateCustomerForm to call setActiveTab('existing')
alongside setOpen(false). Keep the existing onOpenChange reset for user-driven
closures.

---

Outside diff comments:
In
`@packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx`:
- Around line 44-48: Disable the globally configured keep-previous-data behavior
for the identity-keyed query in the customer address selector by setting
placeholderData to undefined in the useQuery options for customerAddresses. Keep
the customerId-based queryKey and existing loading behavior unchanged.
- Around line 44-54: Prevent the selected tab from changing when the
customer-address query transitions from loading to loaded. Update the
tab-selection logic around canSelectExisting and effectiveTab in the customer
address selector to preserve an explicitly chosen “new” tab during loading and
after addresses are fetched, only defaulting to “existing” when appropriate for
a newly initialized customer.

---

Nitpick comments:
In `@packages/dashboard/src/lib/components/shared/customer-selector.tsx`:
- Around line 70-78: Replace the hardcoded English validation messages in
createCustomerFormSchema with localized messages using the component’s
useLingui/t pattern. Ensure the schema is created where the translation function
is available, and apply localized text to the firstName, lastName, and
emailAddress required validations.
- Around line 70-76: Update the createCustomerFormSchema validation rules to use
Zod v4’s error option instead of the deprecated message option for the
firstName, lastName, and emailAddress min-length checks, preserving the existing
validation text and behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 894f94a2-ab65-41a5-9621-be977d4f371e

📥 Commits

Reviewing files that changed from the base of the PR and between 565d8a8 and b8092ae.

📒 Files selected for processing (35)
  • packages/dashboard/e2e/tests/sales/orders.spec.ts
  • packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx
  • packages/dashboard/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx
  • packages/dashboard/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx
  • packages/dashboard/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts
  • packages/dashboard/src/i18n/locales/ar.po
  • packages/dashboard/src/i18n/locales/bg.po
  • packages/dashboard/src/i18n/locales/cs.po
  • packages/dashboard/src/i18n/locales/de.po
  • packages/dashboard/src/i18n/locales/en.po
  • packages/dashboard/src/i18n/locales/es.po
  • packages/dashboard/src/i18n/locales/fa.po
  • packages/dashboard/src/i18n/locales/fr.po
  • packages/dashboard/src/i18n/locales/he.po
  • packages/dashboard/src/i18n/locales/hr.po
  • packages/dashboard/src/i18n/locales/hu.po
  • packages/dashboard/src/i18n/locales/it.po
  • packages/dashboard/src/i18n/locales/ja.po
  • packages/dashboard/src/i18n/locales/nb.po
  • packages/dashboard/src/i18n/locales/ne.po
  • packages/dashboard/src/i18n/locales/nl.po
  • packages/dashboard/src/i18n/locales/pl.po
  • packages/dashboard/src/i18n/locales/pt_BR.po
  • packages/dashboard/src/i18n/locales/pt_PT.po
  • packages/dashboard/src/i18n/locales/ro.po
  • packages/dashboard/src/i18n/locales/ru.po
  • packages/dashboard/src/i18n/locales/sv.po
  • packages/dashboard/src/i18n/locales/tr.po
  • packages/dashboard/src/i18n/locales/uk.po
  • packages/dashboard/src/i18n/locales/uz.po
  • packages/dashboard/src/i18n/locales/zh_Hans.po
  • packages/dashboard/src/i18n/locales/zh_Hant.po
  • packages/dashboard/src/lib/components/shared/customer-address-form.tsx
  • packages/dashboard/src/lib/components/shared/customer-selector.tsx
  • packages/dev-server/graphql/graphql-env.d.ts
🛑 Comments failed to post (1)
packages/dashboard/src/lib/components/shared/customer-selector.tsx (1)

196-283: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

activeTab doesn't reset when the popover is closed programmatically.

onOpenChange resets activeTab to 'existing' only when !isOpen, but the onSelect/onSubmit callbacks close the popover via setOpen(false) directly (bypassing the controlled component's onOpenChange handler, which typically only fires in response to user-driven interactions like outside-click or Escape, not programmatic prop changes). After successfully creating a new customer, activeTab stays 'new'; reopening the popover next time will show the "Create new customer" tab instead of resetting to "Existing customer".

🐛 Suggested fix
                         <TabsContent value="existing">
                             <div className="mt-2 rounded-md border">
                                 <CustomerSearch
                                     onSelect={customer => {
                                         props.onSelect(customer);
                                         setOpen(false);
+                                        setActiveTab('existing');
                                     }}
                                 />
                             </div>
                         </TabsContent>
                         <TabsContent value="new">
                             <div className="mt-2">
                                 <CreateCustomerForm
                                     onSubmit={input => {
                                         props.onCreateNew?.(input);
                                         setOpen(false);
+                                        setActiveTab('existing');
                                     }}
                                 />
                             </div>
                         </TabsContent>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

export function CustomerSelector(props: CustomerSelectorProps) {
    const [open, setOpen] = useState(false);
    const [activeTab, setActiveTab] = useState<string>('existing');

    const trigger = (
        <PopoverTrigger
            render={
                <Button
                    variant="outline"
                    size="sm"
                    type="button"
                    disabled={props.readOnly}
                    className="gap-2"
                />
            }
        >
            <Plus className="h-4 w-4" />
            {props.label ?? <Trans>Select customer</Trans>}
        </PopoverTrigger>
    );

    // When inline creation is not enabled, preserve the original lightweight popover behaviour.
    if (!props.allowCreateNew) {
        return (
            <Popover open={open} onOpenChange={setOpen}>
                {trigger}
                <PopoverContent className="p-0 w-[350px]" align="start">
                    <CustomerSearch
                        onSelect={customer => {
                            props.onSelect(customer);
                            setOpen(false);
                        }}
                    />
                </PopoverContent>
            </Popover>
        );
    }

    return (
        <Popover
            open={open}
            onOpenChange={isOpen => {
                setOpen(isOpen);
                if (!isOpen) {
                    setActiveTab('existing');
                }
            }}
        >
            {trigger}
            <PopoverContent className="w-[420px] p-0" align="start">
                <div className="p-4">
                    <Tabs value={activeTab} onValueChange={setActiveTab}>
                        <TabsList className="grid w-full grid-cols-2">
                            <TabsTrigger value="existing">
                                <Link className="mr-2 h-4 w-4" />
                                <Trans>Existing customer</Trans>
                            </TabsTrigger>
                            <TabsTrigger value="new">
                                <Plus className="mr-2 h-4 w-4" />
                                <Trans>Create new customer</Trans>
                            </TabsTrigger>
                        </TabsList>
                        <TabsContent value="existing">
                            <div className="mt-2 rounded-md border">
                                <CustomerSearch
                                    onSelect={customer => {
                                        props.onSelect(customer);
                                        setOpen(false);
                                        setActiveTab('existing');
                                    }}
                                />
                            </div>
                        </TabsContent>
                        <TabsContent value="new">
                            <div className="mt-2">
                                <CreateCustomerForm
                                    onSubmit={input => {
                                        props.onCreateNew?.(input);
                                        setOpen(false);
                                        setActiveTab('existing');
                                    }}
                                />
                            </div>
                        </TabsContent>
                    </Tabs>
                </div>
            </PopoverContent>
        </Popover>
    );
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/dashboard/src/lib/components/shared/customer-selector.tsx` around
lines 196 - 283, Reset activeTab to 'existing' whenever the popover is closed
programmatically: update the onSelect callback in CustomerSearch and the
onSubmit callback in CreateCustomerForm to call setActiveTab('existing')
alongside setOpen(false). Keep the existing onOpenChange reset for user-driven
closures.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/dashboard/src/i18n/locales/pt_PT.po`:
- Around line 42-44: Translate the newly added customer, address, and
draft-order flow entries in pt_PT.po, replacing every empty msgstr at the
referenced entries with accurate Portuguese translations while preserving the
existing msgid and PO formatting.

In `@packages/dashboard/src/i18n/locales/ro.po`:
- Around line 42-44: Translate every newly added customer/address flow and
draft-order error entry in the Romanian locale, including the primary “Select an
existing customer or create a new one” entry and all referenced locations,
replacing each empty msgstr with accurate, natural Romanian text while
preserving placeholders and formatting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b33fc3a2-9654-4567-9c24-065a1cb8a33f

📥 Commits

Reviewing files that changed from the base of the PR and between b8092ae and cfa605f.

📒 Files selected for processing (34)
  • packages/dashboard/e2e/tests/sales/orders.spec.ts
  • packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx
  • packages/dashboard/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx
  • packages/dashboard/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx
  • packages/dashboard/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts
  • packages/dashboard/src/i18n/locales/ar.po
  • packages/dashboard/src/i18n/locales/bg.po
  • packages/dashboard/src/i18n/locales/cs.po
  • packages/dashboard/src/i18n/locales/de.po
  • packages/dashboard/src/i18n/locales/en.po
  • packages/dashboard/src/i18n/locales/es.po
  • packages/dashboard/src/i18n/locales/fa.po
  • packages/dashboard/src/i18n/locales/fr.po
  • packages/dashboard/src/i18n/locales/he.po
  • packages/dashboard/src/i18n/locales/hr.po
  • packages/dashboard/src/i18n/locales/hu.po
  • packages/dashboard/src/i18n/locales/it.po
  • packages/dashboard/src/i18n/locales/ja.po
  • packages/dashboard/src/i18n/locales/nb.po
  • packages/dashboard/src/i18n/locales/ne.po
  • packages/dashboard/src/i18n/locales/nl.po
  • packages/dashboard/src/i18n/locales/pl.po
  • packages/dashboard/src/i18n/locales/pt_BR.po
  • packages/dashboard/src/i18n/locales/pt_PT.po
  • packages/dashboard/src/i18n/locales/ro.po
  • packages/dashboard/src/i18n/locales/ru.po
  • packages/dashboard/src/i18n/locales/sv.po
  • packages/dashboard/src/i18n/locales/tr.po
  • packages/dashboard/src/i18n/locales/uk.po
  • packages/dashboard/src/i18n/locales/uz.po
  • packages/dashboard/src/i18n/locales/zh_Hans.po
  • packages/dashboard/src/i18n/locales/zh_Hant.po
  • packages/dashboard/src/lib/components/shared/customer-address-form.tsx
  • packages/dashboard/src/lib/components/shared/customer-selector.tsx
✅ Files skipped from review due to trivial changes (8)
  • packages/dashboard/src/i18n/locales/en.po
  • packages/dashboard/src/i18n/locales/nb.po
  • packages/dashboard/src/i18n/locales/zh_Hans.po
  • packages/dashboard/src/i18n/locales/zh_Hant.po
  • packages/dashboard/src/i18n/locales/fr.po
  • packages/dashboard/src/i18n/locales/it.po
  • packages/dashboard/src/i18n/locales/ru.po
  • packages/dashboard/src/i18n/locales/ar.po
🚧 Files skipped from review as they are similar to previous changes (24)
  • packages/dashboard/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts
  • packages/dashboard/src/app/routes/authenticated/orders/orders.$id.modify.tsx
  • packages/dashboard/src/lib/components/shared/customer-address-form.tsx
  • packages/dashboard/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx
  • packages/dashboard/src/i18n/locales/ja.po
  • packages/dashboard/src/i18n/locales/pl.po
  • packages/dashboard/src/i18n/locales/tr.po
  • packages/dashboard/src/i18n/locales/fa.po
  • packages/dashboard/src/i18n/locales/he.po
  • packages/dashboard/src/app/routes/_authenticated/orders/orders.draft.$id.tsx
  • packages/dashboard/e2e/tests/sales/orders.spec.ts
  • packages/dashboard/src/i18n/locales/uz.po
  • packages/dashboard/src/lib/components/shared/customer-selector.tsx
  • packages/dashboard/src/i18n/locales/cs.po
  • packages/dashboard/src/i18n/locales/de.po
  • packages/dashboard/src/i18n/locales/uk.po
  • packages/dashboard/src/i18n/locales/pt_BR.po
  • packages/dashboard/src/i18n/locales/nl.po
  • packages/dashboard/src/i18n/locales/bg.po
  • packages/dashboard/src/i18n/locales/es.po
  • packages/dashboard/src/i18n/locales/hu.po
  • packages/dashboard/src/i18n/locales/hr.po
  • packages/dashboard/src/i18n/locales/ne.po
  • packages/dashboard/src/i18n/locales/sv.po

Comment on lines +42 to +44
#: src/lib/components/shared/customer-selector.tsx:266
msgid "Select an existing customer or create a new one"
msgstr ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate the newly added customer/address flow strings.

These entries still have empty msgstr values, so Portuguese users will see English labels, prompts, and draft-order errors in the new flow. Add translations before merging.

Also applies to: 510-512, 709-711, 739-741, 889-891, 1042-1044, 1161-1163, 1456-1458, 1591-1593, 1734-1736, 2408-2410, 2642-2644, 3448-3450, 3590-3592, 4361-4363, 4536-4538, 4733-4735, 4800-4802, 5843-5845

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/dashboard/src/i18n/locales/pt_PT.po` around lines 42 - 44, Translate
the newly added customer, address, and draft-order flow entries in pt_PT.po,
replacing every empty msgstr at the referenced entries with accurate Portuguese
translations while preserving the existing msgid and PO formatting.

Comment on lines +42 to +44
#: src/lib/components/shared/customer-selector.tsx:266
msgid "Select an existing customer or create a new one"
msgstr ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate the newly added customer/address flow strings.

These entries still have empty msgstr values, so Romanian users will see English labels, prompts, and draft-order errors in the new flow. Add translations before merging.

Also applies to: 482-484, 681-683, 711-713, 861-863, 1010-1012, 1129-1131, 1408-1410, 1538-1540, 1673-1675, 2567-2569, 3352-3354, 3479-3481, 4221-4223, 4380-4382, 4577-4579, 4644-4646, 5635-5637

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/dashboard/src/i18n/locales/ro.po` around lines 42 - 44, Translate
every newly added customer/address flow and draft-order error entry in the
Romanian locale, including the primary “Select an existing customer or create a
new one” entry and all referenced locations, replacing each empty msgstr with
accurate, natural Romanian text while preserving placeholders and formatting.

…ddress selectors

Reset the customer selector tab to 'existing' when a customer is selected
or created (programmatic close does not trigger onOpenChange), and preserve
an explicitly chosen 'new' tab in the address selector while the address
query transitions from loading to loaded.

Relates to vendurehq#4810
@michaelbromley michaelbromley added the T1: Fast track Clearly understood fix with limited blast radius. Fast lane. label Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T1: Fast track Clearly understood fix with limited blast radius. Fast lane.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Draft order: allow creating a customer and address inline in the React dashboard (parity with Angular admin-ui)

2 participants